/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "IpcClient.h"
#include "IpcResult.h"
#include "IpcApi.h"
#include "IpcAsyncRequest.h"
#include "stdio.h"
#include "IpcClientLoader.h"
#include "OSMemory.h"
#include "ParameterCheck.h"
#include "IpcRequest.h"

#define DEFAULT_TIMEOUT     3000

#define MSG_ID_ALLOC_CLIENT             0
#define MSG_ID_REMOVE_CLIENT            1

static IPC_API_RESULT_T IpcResultToIpcApiResult(int ipcResult) {
    IPC_API_RESULT_T result = IPC_API_RESULT_OK;
    switch(ipcResult) {
        case IPC_RESULT_ERROR:
            result = IPC_API_RESULT_ERROR;
            break;
        case IPC_RESULT_TIMEOUT:
            result = IPC_API_RESULT_TIMEOUT;
            break;
        case IPC_RESULT_OK:
            result = IPC_API_RESULT_OK;
            break;

        default:
            result = IPC_API_RESULT_ERROR;
            break;
    }

    return result;
}

IpcClient::IpcClient(int subId, int serviceId) : mSubId(subId), mServiceId(serviceId), mClientId(-1) {
    mRequestTimeout = DEFAULT_TIMEOUT;
    mSyncRequestSignal = newSignal();
    mChannelManager = getDefaultIpcChannelManager();
    mClientManager = getDefaultClientManager();
    mChannel = getChannel(mChannelManager, subId);
}

IpcClient::~IpcClient() {

}

IPC_API_RESULT_T IpcClient::bindSubscription() {
    BindClientDataInfo info = {mServiceId, mSubId};
    return sendSyncRequest(0, 0, &info, sizeof(BindClientDataInfo), &mClientId, sizeof(mClientId));
}

void sendChannelRequestData(IpcChannel* channel, IpcHeadInfo* header, void* data, int dataLen) {
    PARAMTER_CHECK_RETURN_VOID(channel);
    int8_t* buffer = (int8_t*)OS_MALLOC(sizeof(IpcHeadInfo) + dataLen);
    PARAMTER_CHECK_RETURN_VOID(buffer);
    int8_t* temp = buffer;
    int bufferLen = 0;
    memcpy(temp, header, sizeof(IpcHeadInfo));
    bufferLen += sizeof(IpcHeadInfo);
    temp += sizeof(IpcHeadInfo);
    if (data && dataLen > 0) {
        memcpy(temp, data, dataLen);
        bufferLen += dataLen;
    }

    channel->sendData(channel, buffer, bufferLen);
}

IPC_API_RESULT_T IpcClient::sendAsyncRequest(int msgId, void* data, int dataLen, IpcAsyncRequest* userData) {
    IpcRequestType* request = OS_ALLOC_AND_RESET(IpcRequestType);
    PARAMTER_CHECK_RETURN_VALUE(request, IPC_API_RESULT_NO_MEMEORY);
    request->req.async.mUserData = userData;
    request->asyncReq = true;

    IpcHeadInfo header;
    header.clientId = mClientId;
    header.msgId = msgId;
    header.serviceId = mServiceId;
    header.transaction = mClientManager->nextTransaction();
    mClientManager->addTransaction(header.transaction, request);
    sendChannelRequestData(mChannel, &header, data, dataLen);
    return IPC_API_RESULT_OK;
}


IPC_API_RESULT_T IpcClient::sendSyncRequest(int msgId, void* reqData, int reqDataLen, void* rspData, int rspDataLen) {
    return sendSyncRequest(msgId, mServiceId, reqData, reqDataLen, rspData, rspDataLen);
}

IPC_API_RESULT_T IpcClient::sendSyncRequest(int msgId, int serviceId, void* reqData, int reqDataLen, void* rspData, int rspDataLen) {
    IpcRequestType* request = OS_ALLOC_AND_RESET(IpcRequestType);
    PARAMTER_CHECK_RETURN_VALUE(request, IPC_API_RESULT_NO_MEMEORY);
    request->req.sync.signal = mSyncRequestSignal;
    request->req.sync.rspData = rspData;
    request->req.sync.rspDataLen = rspDataLen;
    request->req.sync.result = IPC_RESULT_TIMEOUT;
    request->asyncReq = false;

    IpcHeadInfo header;
    header.clientId = mClientId;
    header.msgId = msgId;
    header.serviceId = serviceId;
    header.transaction = mClientManager->nextTransaction();
    mClientManager->addTransaction(header.transaction, request);
    sendChannelRequestData(mChannel, &header, reqData, reqDataLen);
    signalWait(mSyncRequestSignal, mRequestTimeout);
    return IpcResultToIpcApiResult(request->req.sync.result);
}


void ipcClientHandleRsp(int serviceId, int msgId, int result, void* data, int dataLen, void* userData) {
    IpcRequestType* request = (IpcRequestType*) userData;
    PARAMTER_CHECK_RETURN_VOID(request);

    /*
    if (serviceId == 0) {
        printf("ipcClientHandleRsp serviceId id %d result %d\n", serviceId, result);
        ((IpcClient*)request)->handleMgrServiceRsp(msgId, result, data, dataLen, userData);
    }*/

    /* handle sync request, notify signal to sender*/
    if (!request->asyncReq) {
        request->req.sync.result = result;
        if (dataLen > request->req.sync.rspDataLen) {
            result = IPC_RESULT_EXCEED_MAX_SIZE;
        }

        if (result == IPC_RESULT_OK) {
            memcpy(request->req.sync.rspData, data, dataLen);
        }
        setSignal(request->req.sync.signal);
    } else if (request->req.async.mUserData) { /* handle async request, invoke the callback */
        IpcAsyncRequest* asyncRequest = (IpcAsyncRequest*)request->req.async.mUserData;
        if (result != IPC_RESULT_OK && asyncRequest->mServiceCb) {
            asyncRequest->mServiceCb(IpcResultToIpcApiResult(result), NULL, 0, asyncRequest->mUserData);
        } else if (asyncRequest->mResultCb) {
            asyncRequest->mResultCb(msgId, result, data, dataLen);
        }
        delete asyncRequest;
    }

    OS_FREE(request);
}
